package com.ansion.commonchat.startup.workmanager.bwork.impl;

import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.ansion.commonchat.startup.workmanager.bwork.BOneTimeWorkRequest;
import com.ansion.commonchat.startup.workmanager.bwork.BOperation;
import com.ansion.commonchat.startup.workmanager.bwork.BWorkContinuation;
import com.ansion.commonchat.startup.workmanager.bwork.BWorkRequest;
import com.ansion.commonchat.startup.workmanager.bwork.utils.BEnqueueRunnable;
import com.ansion.log.LogTag;
import com.ansion.log.VLog;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


/**
 * Created by wanghongjie on 2023/5/10
 * Describe : 任务链实现类
 * <p>
 * ExistingWorkPolicy 重复替代规则选择默认 ExistingWorkPolicy.KEEP 策略。
 * 并在 EnqueueRunnable#enqueueWorkWithPrerequisites 中只保留了此策略的逻辑。
 * 故在本实现类中去除了 ExistingWorkPolicy 属性。
 */
public class BWorkContinuationImpl extends BWorkContinuation {
    private final BWorkManagerImpl mWorkManagerImpl;
    private final List<? extends BWorkRequest> mWork;
    private final List<String> mIds;
    private final List<String> mAllIds;
    private final List<BWorkContinuationImpl> mParents;

    private boolean mEnqueued;
    private BOperation mOperation;

    @NonNull
    public BWorkManagerImpl getWorkManagerImpl() {
        return mWorkManagerImpl;
    }

    @NonNull
    public List<? extends BWorkRequest> getWork() {
        return mWork;
    }

    @NonNull
    public List<String> getIds() {
        return mIds;
    }

    public List<String> getAllIds() {
        return mAllIds;
    }

    public boolean isEnqueued() {
        return mEnqueued;
    }

    public void markEnqueued() {
        mEnqueued = true;
    }

    public List<BWorkContinuationImpl> getParents() {
        return mParents;
    }

    public BWorkContinuationImpl(
            @NonNull BWorkManagerImpl workManagerImpl,
            @NonNull List<? extends BWorkRequest> work) {
        this(
                workManagerImpl,
                work,
                null);
    }

    public BWorkContinuationImpl(@NonNull BWorkManagerImpl workManagerImpl,
                                 @NonNull List<? extends BWorkRequest> work,
                                 @Nullable List<BWorkContinuationImpl> parents) {
        mWorkManagerImpl = workManagerImpl;
        mWork = work;
        mParents = parents;
        mIds = new ArrayList<>(mWork.size());
        mAllIds = new ArrayList<>();
        if (parents != null) {
            for (BWorkContinuationImpl parent : parents) {
                mAllIds.addAll(parent.mAllIds);
            }
        }
        for (int i = 0; i < work.size(); i++) {
            String id = work.get(i).getStringId();
            mIds.add(id);
            mAllIds.add(id);
        }
    }

    @NonNull
    @Override
    public BWorkContinuation then(@NonNull List<BOneTimeWorkRequest> work) {
        if (work.isEmpty()) {
            return this;
        } else {
            return new BWorkContinuationImpl(mWorkManagerImpl,
                    work,
                    Collections.singletonList(this));
        }
    }

    @NonNull
    @Override
    public BOperation enqueue() {
        if (!mEnqueued) {
            BEnqueueRunnable runnable = new BEnqueueRunnable(this);
            mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable);
            mOperation = runnable.getOperation();
        } else {
            if (VLog.OPEN_LOG)
                VLog.w(LogTag.TAG_B_WORK_MANAGER, "Already enqueued work ids (%s)", TextUtils.join(", ", mIds));
        }
        return mOperation;
    }

    /**
     * 用于检查WorkContinuationImpl是否存在循环依赖。
     * 它调用了hasCycles(WorkContinuationImpl continuation, Set<String> visited)方法进行实际的循环检测。
     */
    public boolean hasCycles() {
        return hasCycles(this, new HashSet<String>());
    }

    /**
     * 通过递归地遍历父级WorkContinuationImpl对象来进行检测。
     * 使用visited集合来跟踪已经访问过的WorkContinuationImpl对象的标识符，避免重复访问。
     * 如果存在循环依赖，将返回true，否则返回false。
     */
    private static boolean hasCycles(
            @NonNull BWorkContinuationImpl continuation,
            @NonNull Set<String> visited) {

        visited.addAll(continuation.getIds());

        Set<String> prerequisiteIds = prerequisitesFor(continuation);
        for (String id : visited) {
            if (prerequisiteIds.contains(id)) {
                return true;
            }
        }

        List<BWorkContinuationImpl> parents = continuation.getParents();
        if (parents != null && !parents.isEmpty()) {
            for (BWorkContinuationImpl parent : parents) {
                if (hasCycles(parent, visited)) {
                    return true;
                }
            }
        }

        visited.removeAll(continuation.getIds());
        return false;
    }

    /**
     * 返回给定WorkContinuationImpl对象的前置条件集合。
     * 它通过遍历父级WorkContinuationImpl对象来获取前置条件的标识符，并将这些标识符添加到一个Set集合中返回。
     */
    @NonNull
    public static Set<String> prerequisitesFor(BWorkContinuationImpl continuation) {
        Set<String> preRequisites = new HashSet<>();
        List<BWorkContinuationImpl> parents = continuation.getParents();
        if (parents != null && !parents.isEmpty()) {
            for (BWorkContinuationImpl parent : parents) {
                preRequisites.addAll(parent.getIds());
            }
        }
        return preRequisites;
    }

}
